#include <IDK.hpp>


#include <Resources.hpp>
#include <Shader.hpp>
#include <Material.hpp>
#include <Texture.hpp>
#include <Mesh.hpp>

#include <RenderTarget.hpp>

#include <Core.hpp>

#include <Convert.hpp>

#include <CoreModule.hpp>

namespace Core
{
	template<class T>
	class TFindInCoreByPath : public TComparer<T, String>
	{
		typedef TComparer<T, String> Base;
	public:
		TFindInCoreByPath(String cmp) : Base(cmp)
		{}
		inline virtual bool Compare(T& src) const
		{
			return src->GetPath() == Base::mCompare;
		}
	};

	template<class T>
	class TFindInCoreByName : public TComparer<T, String>
	{
		typedef TComparer<T, String> Base;
	public:
		TFindInCoreByName(String cmp) : Base(cmp)
		{}
		inline virtual bool Compare(T& src) const
		{
			return src->GetName() == Base::mCompare;
		}
	};

	TCore::TCore()
		: Base(),
		mResMananager(new ResourceManager::Create()), 
		mMeshes(new Array<Mesh>::Create()),
		mMaterials(new Array<Material>::Create()),
		mTextures(new Array<Texture>::Create())
	{
	}

	//-------------------------------------------------------
	// OnInit
	//-------------------------------------------------------
	void TCore::OnInit(void)
	{
		//prepare
		mResMananager->Initialize();
	}

	//-------------------------------------------------------
	// OnStart
	//-------------------------------------------------------
	void TCore::OnStart(void)
	{
		ThreadStart();
	}

	//-------------------------------------------------------
	// OnUpdate
	//-------------------------------------------------------
	void TCore::OnUpdate(float dt)
	{
		Base::Update(dt);
	}

	//-------------------------------------------------------
	// OnFinish
	//-------------------------------------------------------
	void TCore::OnFinish(void)
	{
		ThreadSyncExit();

		Clear();
	}

	//-------------------------------------------------------
	// AddMesh
	//-------------------------------------------------------
	ref<IDK::IMesh>	TCore::GetMesh(const String& path)
	{
		const String dir = mDefaultResourcePath +"Meshes/";
		const String ext = ".bin";

		Mesh find = mMeshes->Find(new TFindInCoreByPath<Mesh>(path));

		if(find != NullPtr)
		{
			return find.As<IDK::IMesh>();
		}

		Mesh mesh(new Mesh::Create(path));
		{
			String buildpath = dir + path + "_00" + ext;
			ref<IDK::IStream> stream = mResMananager->GetStream(buildpath);
			MeshLOD meshLOD(new MeshLOD::Create(*this, stream));
			mesh->AddLOD(meshLOD.As<MeshLOD::Instance>());
			mResMananager->AddResource(meshLOD.As<IDK::IResource>());
		}
		mMeshes->Add(mesh);
		return mesh.As<IDK::IMesh>();
	}

	ref<IDK::IShader> TCore::GetShader(const String &path)
	{
		const String dir = mDefaultResourcePath +"Shaders/";
		const String pr = "/program";
		String buildpath = dir + path + pr + "_00.";
		//
		iLog(buildpath->Chars());
		Shader program = new Shader::Create(*this, NullPtr);
		program->SetShaderSource(IDK::IShader::ProgramType::Vertex, mResMananager->GetStream(buildpath + "vertex"));
		program->SetShaderSource(IDK::IShader::ProgramType::Fragment, mResMananager->GetStream(buildpath + "fragment"));
		//return
		return program.As<IDK::IShader>();
	}

	//-------------------------------------------------------
	// GetRenderTarget
	//-------------------------------------------------------
	ref<IDK::IRenderTarget> TCore::GetRenderTarget(const String& name)
	{
		RenderTarget rtt = new RenderTarget::Create(name);
		return rtt.As<IDK::IRenderTarget>();
	}

	//-------------------------------------------------------
	// GetMaterial
	//-------------------------------------------------------
	ref<IDK::IMaterial> TCore::GetMaterial(const String& name)
	{
		Material item = mMaterials->Find(new TFindInCoreByName<Material>(name));

		if(item != NullPtr)
		{
			return item.As<IDK::IMaterial>();
		}

		item = new Material::Create(name);
		mMaterials->Add(item);
		return item.As<IDK::IMaterial>();
	}
	//-------------------------------------------------------
	// CreateRenderTexture
	//-------------------------------------------------------
	ref<IDK::IRenderTexture> TCore::CreateRenderTexture(uint width, uint height, IDK::Pixel::Type::Enum pixelType)
	{
		switch(pixelType)
		{
		case IDK::Pixel::Type::RGBA8:
			{
				RenderTexture rTexture = new RenderTexture::Create(width, height, pixelType);
				return rTexture.As<IDK::IRenderTexture>();
			}
		default:
			iError("PixelType is not support");
		}

		return NullPtr;
	}
	//-------------------------------------------------------
	// AddTexture
	//-------------------------------------------------------
	ref<IDK::ITexture> TCore::AddTexture(const String& path, IDK::Pixel::Type::Enum pixelType, uint mipCount, uint width, uint height)
	{
		Texture find = mTextures->Find(new TFindInCoreByPath<Texture>(path));

		if(find != NullPtr)
		{
			return find.As<IDK::ITexture>();
		}

		const String dir = mDefaultResourcePath +"Textures/";

		const pwchar pExt = TextureCodec::Ext();

		const String ext(pExt);

		Texture texture = new Texture::Create(path, pixelType);

		for(uint i = 0; i!=mipCount; ++i)
		{
			wchar c = '0'+(wchar)i;
			String strIndex;
			strIndex += c;
			String buildpath = dir + path + "_0" + strIndex + ext;
			ref<IDK::IStream> stream = mResMananager->GetStream(buildpath);
			TextureMip mip = new TextureMip::Create(*this, stream, pixelType, IDK::IResource::NormalPriority - IDK::ITexture::MaxMipCount + i, width, height);
			texture->AddMip(mip);
			mResMananager->AddResource(mip.As<IDK::IResource>());
			width /= 2;
			height /= 2;
		}
		mTextures->Add(texture);
		return texture.As<IDK::ITexture>();
	}
	//-------------------------------------------------------
	// OnThreadStart
	//-------------------------------------------------------
	void TCore::OnThreadStart()
	{
	}

	//-------------------------------------------------------
	// OnThreadStop
	//-------------------------------------------------------
	void TCore::OnThreadStop()
	{
		Base::OnThreadStop();
	}

	//-------------------------------------------------------
	// OnThreadUpdate
	//-------------------------------------------------------
	bool TCore::OnThreadUpdate(float dt, bool ready)
	{
		ready = mResMananager->OnUpdate(dt);
		return Base::OnThreadUpdate(dt, ready);
	}

	//-------------------------------------------------------
	// ReadyForProcessing
	//-------------------------------------------------------
	void TCore::ReadyForProcessing()
	{
		mResMananager->ToProcess();
		Base::ReadyForProcessing(); //tell to thread wakeup
	}

	//-------------------------------------------------------
	// OnActive
	//-------------------------------------------------------
	void TCore::OnActive(bool/* active*/)
	{
	}

	//-------------------------------------------------------
	// SetDefaultResourcePath
	//-------------------------------------------------------
	void TCore::SetDefaultResourcePath(const String& path)
	{
		mDefaultResourcePath = path;
	}

	//-------------------------------------------------------
	// ::Lock
	//-------------------------------------------------------
	void TCore::Lock(bool/* reset*/)
	{
		ThreadLock();
	}

	//-------------------------------------------------------
	// ::Unlock
	//-------------------------------------------------------
	void TCore::Unlock(void)
	{
		ThreadUnlock();
	}

	//-------------------------------------------------------
	// ::Clear
	//-------------------------------------------------------
	void TCore::Clear(void)
	{
		mMeshes->Clear();
		mMaterials->Clear();
		mTextures->Clear();
		mResMananager->Clear();
		iLog("Cleanup...");
	}

	IDK::Stream TCore::GetStream(String path)
	{
		return mResMananager->GetStream(mDefaultResourcePath + path);
	}

	IDK::ICore* CoreModule::Create()
	{
		return new Core::Instance();
	}
}
/*
//Initialization
extern "C" __declspec(dllexport) IDK::IModule * __InitModule()
{
	return new Core::Core::Instance();
}
*/